<!doctype html>
<title>TextFragment invoked on redirects</title>
<meta charset=utf-8>
<meta name="timeout" content="long">
<link rel="help" href="https://wicg.github.io/ScrollToTextFragment/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="/common/utils.js"></script>
<script src="stash.js"></script>

<!--See comment in scroll-to-text-fragment.html for why these tests have the
structure they do. -->
<script>
// This test ensure correct operation of text-fragments through both HTTP and
// client side redirects in various scenarios.

// Constructs a URL to either redirect.py or the local client-redirect.html;
// which will cause an HTTP or client based redirect, respectively, to
// |to_url|. |type| provides a numeric 30x code to specify an HTTP redirect,
// "location" for a write to window.location, or "meta" for a <meta> refresh.
function buildRedirectUrl(to_url, type) {
  let dest = "";
  to_url = encodeURIComponent(to_url);

  if (typeof type == "number") {
    // If the type is a number, it's an HTTP response code, use redirect.py to
    // respond with an HTTP redirect.
    const code = type;
    dest = `${get_host_info().ORIGIN}/common/redirect.py?status=${code}&location=${to_url}`;
  } else if (type == 'meta' || type == 'location') {
    // Otherwise we're requesting a client-side redirect, either a <meta> tag
    // or window.location. Use the client-redirect file to bounce to the
    // destination.
    dest = `client-redirect.html?${type}&${to_url}`;
  }
  return dest;
}

// Turns |path| from a relative-to-this-file path into a full URL.
function relativePathToFull(path) {
  const pathname = window.location.toString();
  const base_path = pathname.substring(0, pathname.lastIndexOf('/') + 1);
  return base_path + path;
}

const status_codes = [301, 302, 303, 307, 308];

// Test that an HTTP redirect to a URL with a text fragment invokes the
// fragment.
for (let code of status_codes) {
  promise_test(t => new Promise((resolve, reject) => {
    let key = token();

    const abs_url = relativePathToFull(`redirects-target.html?key=${key}#:~:text=target`);
    const url = buildRedirectUrl(abs_url, code);

    test_driver.bless('Open a URL with a text fragment directive', () => {
      window.open(url, '_blank', 'noopener');
    });

    fetchResults(key, resolve, reject);
  }).then(data => {
    assert_equals(data.scrolled, true);
  }), `Text fragment works from HTTP ${code} redirect.`);
}

// Test that a URL with a text fragment that causes an HTTP redirect preserves
// the fragment and invokes it on the destination page.
for (let code of status_codes) {
  promise_test(t => new Promise((resolve, reject) => {
    let key = token();

    const abs_url = relativePathToFull(`redirects-target.html?key=${key}`);
    const url = buildRedirectUrl(abs_url, code) + "#:~:text=target";

    test_driver.bless('Open a URL with a text fragment directive', () => {
      window.open(url, '_blank', 'noopener');
    });

    fetchResults(key, resolve, reject);
  }).then(data => {
    assert_equals(data.scrolled, true);
  }), `Text fragment propagated through HTTP ${code} redirect.`);
}

// Test that client-side redirects (using script) to a URL with a text fragment
// cause the text fragment to be invoked.
for (let type of ['location', 'meta']) {
  promise_test(t => new Promise((resolve, reject) => {
    let key = token();

    const to_url = `redirects-target.html?key=${key}#:~:text=target`
    const url = buildRedirectUrl(to_url, type);

    test_driver.bless('Open a URL with a text fragment directive', () => {
      window.open(url, '_blank', 'noopener');
    });

    fetchResults(key, resolve, reject);
  }).then(data => {
    assert_equals(data.scrolled, true);
  }), `Text fragment works on client-side ${type} redirect.`);
}

// Test that client-side redirects (using script) to a URL with a text fragment
// cause the text fragment to be invoked only the first time. A further
// redirect without a user gesture is blocked.
for (let type of ['location', 'meta']) {
  promise_test(t => new Promise((resolve, reject) => {
    let key = token();

    const to_url = `redirects-target.html?twice&key=${key}#:~:text=target`
    const url = buildRedirectUrl(to_url, type);

    test_driver.bless('Open a URL with a text fragment directive', () => {
      window.open(url, '_blank', 'noopener');
    });

    fetchResults(key, resolve, reject);
  }).then(data => {
    assert_equals(data.scrolled, false);
  }), `One text fragment per user gesture allowed in client-side ${type} redirect.`);
}
</script>
